package workgroup.osaka.DirectLink;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;

import org.apache.http.conn.util.InetAddressUtils;

import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.NetworkInfo;
import android.net.Uri;
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pDeviceList;
import android.net.wifi.p2p.WifiP2pInfo;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager.ActionListener;
import android.net.wifi.p2p.WifiP2pManager.Channel;
import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener;
import android.net.wifi.p2p.WifiP2pManager.PeerListListener;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;

/*
 * 	マニフェストの方に記述が必要
 *
 * Wi-FiのＯＮ・ＯＦＦを切り替えるのに必要
 *  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
 * Wi-Fiの状態を参照するのに必要
 *  <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
 */

public class WiFiDirect extends BroadcastReceiver{

	//画面のアクティビティ
	DirectLinkMainActivity activity;
	
	//相手に送信する画像のＵＲＩ
	Uri SendPicUri = null;

	//WifiDirect接続に必要なクラス
	//WifiDirect全般の情報を持っているクラス
	private WifiP2pManager Manager;
	//Wifi接続チャンネル
	private Channel Channel = null;
	//IntentFilterに登録したアクションがレシーバーに通知される
	private IntentFilter IntentFilter;
	//自分が接続中か？・オーナーか？などの情報を持っている
	private WifiP2pInfo info = null;
	//接続先リスト
	private ArrayList<WifiP2pDevice> deviceList = new ArrayList<WifiP2pDevice>();

	//今接続中かどうかを表す true の時しか送信ボタンを押せない
	boolean connect = false;
	
	//自分の端末情報
	WifiP2pDevice mydevice = null;
	private String my_name = "";
	private String my_status = "";
	private String my_mac = "";
	private String my_group = "";
	
	//接続相手の端末情報
	WifiP2pDevice peerdevice = null;
	private String peer_name = "";
	private String peer_status = "";
	private String peer_mac = "";
	private String peer_group = "";

	//リスナー
	private WiFiPeerList WiFiPeerList = new WiFiPeerList();
	private WiFiConnectionInfo WiFiConnectionInfo  = new WiFiConnectionInfo();
	
	//クライアントＩＰ
	static String ClientIP = null;

	public WiFiDirect(DirectLinkMainActivity activity) {
		//Activity
		this.activity = activity;
		
		//インテント フィルタを作成し、ブロードキャスト レシーバがチェックするものと同じインテントを追加します。
		IntentFilter = new IntentFilter();

		//WIFI_P2P_STATE_CHANGED_ACTION デバイスで Wi-Fi ダイレクトが有効または無効になったときのブロードキャスト。
		IntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);

		//WIFI_P2P_PEERS_CHANGED_ACTION discoverPeers() を呼び出したときのブロードキャスト。
		//このインテントをアプリケーションでハンドルしていれば、requestPeers() を呼び出してピアのリストを更新するといいでしょう。
		IntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);

		//WIFI_P2P_CONNECTION_CHANGED_ACTION デバイスの Wi-Fi 接続の状態が変更したときのブロードキャスト。
		IntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);

		//WIFI_P2P_THIS_DEVICE_CHANGED_ACTION デバイス名などのデバイスの詳細が変更されたときのブロードキャスト。
		IntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);

		//WifiP2pManager作成
		Manager = (WifiP2pManager) this.activity.getSystemService(Context.WIFI_P2P_SERVICE);
		Log.d("コンストラクタ", "WifiP2pManager作成");

		//チャンネル取得
		Channel = Manager.initialize(this.activity, this.activity.getMainLooper(), null);
		Log.d("コンストラクタ", "チャンネル取得");
		//Disconnect();

		//ブロードキャストレシーバの登録
		this.activity.registerReceiver(this,IntentFilter);
		Log.d("コンストラクタ", "レシーバ登録");

		//検索する
		Manager.discoverPeers(Channel, null);
		Log.d("コンストラクタ", "検索");

		//各ボタンの動作設定
		SearchButton();
		ConnectButton();
		SendButton();

	}
	
	//自端末と相手端末の情報を取得する
	String[] getStatus(){
		String[] status = new String[8];
		//自分の端末情報
		status[0] = my_name;
		status[1] = my_status;
		status[2] = my_mac;
		status[3] = my_group;
		
		//接続相手の端末情報
		status[4] = peer_name;
		status[5] = peer_status;
		status[6] = peer_mac;
		status[7] = peer_group;
		return status;
		
	}

	//検索結果の先頭の端末情報取得(ログ用)
	WifiP2pDevice getWifiP2pDevice() {
		Log.d("getWifiP2pDevice", "検索結果の先頭の端末情報取得");
		//見つからない場合 null
		if(deviceList.size() <= 0){
			return null;
		}
		return deviceList.get(0);
	}

	//画像ファイルを送信する
	void SendPicture(Uri uri){
		//オーナーの場合
		if(info.isGroupOwner == true){
			//FileTransferServiceを起動する
			Log.d("SendPicture", "画像送信");
			Intent intent = new Intent(activity, FileTransferService.class );
			intent.setAction(FileTransferService.ACTION_SEND_FILE);
			intent.putExtra(FileTransferService.EXTRAS_FILE_PATH, uri.toString());
			Log.d("SendPicture", "uri = " + uri);
			intent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_ADDRESS, ClientIP);
			intent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_PORT, 8988);
			activity.startService(intent);
		//クライアントの場合
		}else{
			//FileTransferServiceを起動する
			Log.d("SendPicture", "画像送信");
			Intent intent = new Intent(activity, FileTransferService.class );
			intent.setAction(FileTransferService.ACTION_SEND_FILE);
			intent.putExtra(FileTransferService.EXTRAS_FILE_PATH, uri.toString());
			Log.d("SendPicture", "uri = " + uri);
			intent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_ADDRESS,info.groupOwnerAddress.getHostAddress());
			intent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_PORT, 8988);
			activity.startService(intent);
		}
	}

	//相手端末と切断する
	void Disconnect(){
		//ソケットを閉じる
		FileServerAsyncTask.close();
		//Wi-FiDirectを切断する
		Manager.removeGroup(Channel, null);
		Log.d("Disconnect", "切断");
	}

	//検索ボタンの内容を設定する
	void SearchButton(){
		activity.ChangeButton("検索" , new SearchButton() , DirectLinkMainActivity.SEARCH_BUTTON);
		Log.d("SearchButton", "検索ボタン設定");
	}

	//接続ボタンの内容を設定する
	void ConnectButton(){
		activity.ChangeButton("接続" , new ConnectButton() , DirectLinkMainActivity.CONNECT_BUTTON);
		Log.d("ConnectButton", "接続ボタン設定");
	}

	//切断ボタンの内容を設定する
	void DisconnectButton(){
		activity.ChangeButton("切断" , new DisconnectButton() , DirectLinkMainActivity.CONNECT_BUTTON);
		Log.d("DisconnectButton", "切断ボタン設定");
	}

	//送信ボタンの内容を設定する
	void SendButton(){
		activity.ChangeButton("送信" , new SendButton() , DirectLinkMainActivity.SEND_BUTTON);
		Log.d("SendButton", "送信ボタン設定");
	}

	//検索ボタンを押した時の内容
	class SearchButton implements OnClickListener{
		// ボタンがクリックされた時
		@Override
		public void onClick(View v) {
			// クリックされた時の処理を記述
			Log.d("SearchButton", "検索ボタンクリック");
			
			//接続していなかったら
			if(connect == false){
				//相手端末情報をクリア
				Log.d("SearchButton", "相手端末情報クリア");
				peer_name = "";
				peer_status = "";
				peer_mac = "";
			}
			
			//discoverPeers() ピアの発見を開始します。
			Manager.discoverPeers(Channel, null);
		}
	}

	//接続ボタンを押した時の内容
	class ConnectButton implements OnClickListener{
		// ボタンがクリックされた時
		@Override
		public void onClick(View v) {
			// クリックされた時の処理を記述
			Log.d("ConnectButton", "接続ボタンクリック");

			//接続時の設定
			WifiP2pConfig config = new WifiP2pConfig();

			//Wi-FiDirectの接続許可ダイアログを相手の画面に出す設定
			//config.wps.setup = WpsInfo.PBC;
			//ＰＩＣコード出す設定？
			config.wps.setup = WpsInfo.DISPLAY;

			Log.d("接続", "グループオーナーインテント変更前：" +config.groupOwnerIntent);
			//グループオーナーインテントの初期値は-1
			//Wi-FiDirectの親機として接続
			//config.groupOwnerIntent = 15;
			//Wi-FiDirectの子機として接続
			config.groupOwnerIntent = 0;
			Log.d("接続", "グループオーナーインテント変更後：" +config.groupOwnerIntent);

			//検索結果の先頭の端末情報取得
			peerdevice = getWifiP2pDevice();

			//デバイスが見つからなかった場合
			if(peerdevice == null){
				return;
			}

			//接続先情報取得
			config.deviceAddress = peerdevice.deviceAddress;
			Log.d("接続", peerdevice.deviceAddress + "へ接続を試みます");

			//connect() 特定の設定を持つデバイスでピア・ツー・ピア接続を開始します。
			Manager.connect(Channel, config, new ActionListener() {

				public void onSuccess() {
					// 接続が成功した場合
					Log.d("接続", "接続リクエスト送信");
				}

				public void onFailure(int reason) {
					// 接続が成功した場合
					Log.d("接続", "接続リクエスト失敗");
				}
			});

			//検索結果の先頭の端末情報取得
			peerdevice = getWifiP2pDevice();
			//相手端末の情報を更新する
			PeerStatus();

		}
	}

	//接続後、接続ボタンを切断ボタンに変える時に使用
	class DisconnectButton implements OnClickListener{
		// ボタンがクリックされた時
		@Override
		public void onClick(View v) {
			// クリックされた時の処理を記述
			Log.d("DisconnectButton", "切断ボタンクリック");
			//Wi-FiDirectを切断する
			Disconnect();
		}
	}

	//送信ボタンを押した時の内容
	class SendButton implements OnClickListener{
		// ボタンがクリックされた時
		@Override
		public void onClick(View v) {
			// クリックされた時の処理を記述
			Log.d("SendButton", "送信ボタンクリック");
			if(connect == true){
				if(ClientIP != null){
					
					//送信確認のダイアログを出す
					SendPicDialog();
					
				}else{
					//表示
					Toast.makeText(activity,
							"送信準備ができていません",
							Toast.LENGTH_SHORT).show();
					Log.d("SendButton", "送信準備ができていません");
				}
				
			}else{
				//表示
				Toast.makeText(activity,
						"送信相手無し",
						Toast.LENGTH_SHORT).show();
				Log.d("SendButton", "送信相手無し");
			}
		}
	}

	//接続先リストの作成
	class WiFiPeerList implements PeerListListener {
		//ピアーのリスト作成
		@Override
		public void onPeersAvailable(WifiP2pDeviceList peerList) {
			Log.d("onPeersAvailable", "接続先リスト作成");
			deviceList.clear();
			deviceList.addAll(peerList.getDeviceList());
			if(deviceList.size() == 0){
				
				Log.d("onPeersAvailable", "デバイスが見つかりませんでした");
				
				//相手端末情報をクリア
				peer_name = "";
				peer_status = "";
				peer_mac = "";
				
				//discoverPeers() ピアの発見を開始します。
				Manager.discoverPeers(Channel, null);
				return;
			}
			//検索結果の先頭の端末情報取得
			peerdevice = getWifiP2pDevice();
			//相手端末の情報を更新する
			PeerStatus();
		}
	}
	
	//受信側の接続準備ができた時にここに来る
	class WiFiConnectionInfo implements ConnectionInfoListener{
		@Override
		public synchronized void onConnectionInfoAvailable(WifiP2pInfo p2pinfo) {
			Log.d("onConnectionInfoAvailable", "受信準備");
			info = p2pinfo;
				if(ClientIP == null){
					Log.d("onConnectionInfoAvailable", "ClientIPがnull");
					//自分がオーナー
					if(info.isGroupOwner == true){
						//どちらがオーナーか表示する
						Log.d("onConnectionInfoAvailable", "自分がオーナー");
						my_group = "グループオーナー";
						peer_group = "クライアント";
						new FileServerAsyncTask(activity,activity , true).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
					}
					//自分がクライアント
					else{
						//自分のＩＰを相手に送信
						//FileTransferServiceを起動する
						Log.d("onConnectionInfoAvailable", "自分がクライアント");
						Log.d("SendPicture", "クライアントIP送信");
						Intent intent = new Intent(activity, FileTransferService.class );
						intent.setAction(FileTransferService.ACTION_SEND_CLIENTIP);
						intent.putExtra(FileTransferService.CLIENT_IP,getMyIPaddress());
						intent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_ADDRESS,info.groupOwnerAddress.getHostAddress());
						intent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_PORT, 8989);
						activity.startService(intent);
						//どちらがオーナーか表示する
						my_group = "クライアント";
						peer_group = "グループオーナー";
						new FileServerAsyncTask(activity,activity , false).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
					}
				}else{
					Log.d("onConnectionInfoAvailable", "ClientIP = " + ClientIP);
					new FileServerAsyncTask(activity,activity , false).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
				}
		}
	}

	//インテントフィルターに登録したアクションの通知をここで受けて処理を行う
	@Override
	public void onReceive(Context context, Intent intent) {
		Log.d("onReceive", "レシーバー受信");
		String action = intent.getAction();
		// TODO 自動生成されたメソッド・スタブ
		if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {

			// Wi-Fiが使える状態かどうか確認
			int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
			//使える
			if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
				//表示
				Toast.makeText(activity,
						"Wi-FiＯＮ",
						Toast.LENGTH_SHORT).show();
				Log.d("onReceive", "WifiON");
			}
			//使えない
			else {
				//表示
				Toast.makeText(activity,
						"Wi-FiＯＦＦ",
						Toast.LENGTH_SHORT).show();
				Log.d("onReceive", "WifiOFF");
			}
		} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
			// 接続先が見つかったらここに入る
			//表示
			Toast.makeText(activity,
					"接続先が見つかりました",
					Toast.LENGTH_SHORT).show();
			Log.d("onReceive", "接続先が見つかりました");
			//接続先リストの作成
			Manager.requestPeers(Channel, WiFiPeerList);

		} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
			// WiFi Direct通信状態の変更通知です。
			//相手デバイスとのコネクションが繋がれば接続処理を、
			//切れた場合は終了処理を行います。
			Log.d("onReceive", "接続状態変更");
			if (Manager == null) {
				Log.d("onReceive", "マネージャーがnull");
				return;
			}
			
			NetworkInfo networkInfo = (NetworkInfo) intent
					.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
			
			if (networkInfo.isConnected()) {
				Log.d("onReceive", "接続しました");
				
				//接続したら受信準備を行う
				Manager.requestConnectionInfo(Channel, WiFiConnectionInfo);
				
				//接続ボタンを切断ボタンへ
				DisconnectButton();
				//接続している状態
				connect = true;
				Log.d("onReceive", "connect = " + connect);

				//表示
				Toast.makeText(activity,
						"接続しました",
						Toast.LENGTH_SHORT).show();
			}else{
				Log.d("onReceive", "接続無し");
				//切断ボタンを接続ボタンへ
				ConnectButton();
				//接続していない状態
				connect = false;
				//クライアントＩＰをnullに
				ClientIP = null;
				
				//切断
				Disconnect();
				
				//オーナー情報リセット
				my_group = "";
				peer_group = "";

				//表示
				Toast.makeText(activity,
						"接続無し",
						Toast.LENGTH_SHORT).show();
				
				//discoverPeers() ピアの発見を開始します。
				Manager.discoverPeers(Channel, null);
				
			}


			//検索結果の先頭の端末情報取得
			peerdevice = getWifiP2pDevice();
			//相手端末の情報を更新する
			PeerStatus();

		} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
			
			Log.d("onReceive", "自端末状況変更");

			//自分端末の情報を更新する
			MyStatus((WifiP2pDevice) intent.getParcelableExtra( WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));
		}
	}

	//相手端末と自端末の状態を取得する
	String getStatus(WifiP2pDevice device){
		Log.d("getStatus", "端末状況取得");
		if(device.status == WifiP2pDevice.AVAILABLE){
			Log.d("getStatus", "端末状況:接続可能");
			return "接続可能";
		}else if(device.status == WifiP2pDevice.INVITED){
			Log.d("getStatus", "端末状況:接続処理中");
			return "接続処理中";
		}else if(device.status == WifiP2pDevice.CONNECTED){
			Log.d("getStatus", "端末状況:接続中");
			return "接続中";
		}else if(device.status == WifiP2pDevice.FAILED){
			Log.d("getStatus", "端末状況:切断");
			return "切断";
		}else if(device.status == WifiP2pDevice.UNAVAILABLE){
			Log.d("getStatus", "端末状況:接続不可");
			return "接続不可";
		}
		Log.d("getStatus", "端末状況:不明");
		return "不明";
	}

	//自分端末の情報を更新する
	void MyStatus(WifiP2pDevice mydevice){
		Log.d("MyStatus", "自分端末情報を更新する");
		if(mydevice != null){
			Log.d("MyStatus", "mydevice は null ではありません");
			//自端末情報取得			
			my_name = mydevice.deviceName;
			my_status = getStatus(mydevice);
			my_mac = mydevice.deviceAddress;
		}
	}

	//相手端末の情報を更新する
	void PeerStatus(){
		if(peerdevice != null){
			Log.d("PeerStatus", "相手端末情報を更新する");
			//相手端末情報を画面に設定する			
			peer_name = peerdevice.deviceName;
			peer_status = getStatus(peerdevice);
			peer_mac = peerdevice.deviceAddress;
		}
	}

	//自端末のＩＰを取得する
	private String getMyIPaddress() {
		Log.d("getMyIPaddress", "自端末のＩＰを取得する");
		Enumeration<NetworkInterface> netIFs;
		try {
			netIFs = NetworkInterface.getNetworkInterfaces();
			while( netIFs.hasMoreElements() ) {
				NetworkInterface netIF = netIFs.nextElement();
				Enumeration<InetAddress> ipAddrs = netIF.getInetAddresses();
				while( ipAddrs.hasMoreElements() ) {
					InetAddress ip = ipAddrs.nextElement();
					if (!ip.isLoopbackAddress() && InetAddressUtils.isIPv4Address(ip.getHostAddress())) {
						Log.d("MyIP", ip.getHostAddress().toString());
						return ip.getHostAddress().toString();
					}
				}
			}
		} catch (SocketException e) {
			e.printStackTrace();
		}
		return null;
	}

	//アプリ再開時の処理
	void Resume() {
		Log.d("Resume", "アプリ再開");
		//ブロードキャストレシーバの登録
		this.activity.registerReceiver(this,IntentFilter);
	}

	//アプリ終了時の処理
	void Destroy() {
		Log.d("Destroy", "アプリ終了");
		
		//切断する
		Disconnect();
		
		//レシーバーを解除する
		UnregisterReceiver();
		
		//各インスタンスをnullに（メモリ圧迫対策？）
		//画面のアクティビティ
		//activity = null;
		
		//相手に送信する画像のＵＲＩ
		SendPicUri = null;

		Manager = null;
		//Wifi接続チャンネル
		Channel = null;
		//IntentFilterに登録したアクションがレシーバーに通知される
		IntentFilter = null;
		//自分が接続中か？・オーナーか？などの情報を持っている
		info = null;
		//接続先リスト
		deviceList = null;
		
		//自分の端末情報
		mydevice = null;
		
		//接続相手の端末情報
		peerdevice = null;

		//リスナー
		WiFiPeerList = null;
		WiFiConnectionInfo  = null;
		
		//クライアントＩＰ
		ClientIP = null;
	}
	
	//レシストレシーバーを解除する
	void UnregisterReceiver(){
		Log.d("UnregisterReceiver", "レジストレシーバー解除");
		activity.unregisterReceiver(this);
	}

	//アクティビティの画像リストで選んだ画像のＵＲＬをセットする
	void setUri(Uri uri) {
		SendPicUri = uri;
	}
	
	//送信確認ダイアログ表示
	private void SendPicDialog(){
		AlertDialog.Builder dialog = new AlertDialog.Builder(activity);
		dialog.setTitle("確認");
		dialog.setMessage("送信しますか？");
		dialog.setPositiveButton("送信",new DialogInterface.OnClickListener() {

			@Override
			public void onClick(DialogInterface dialog, int which) {
				//画像を送信する
				SendPicture(SendPicUri);
			}
		});
		dialog.setNegativeButton("キャンセル", new DialogInterface.OnClickListener() {

			@Override
			public void onClick(DialogInterface dialog, int which) {
				//表示
				Toast.makeText(activity,
						"送信をキャンセルします",
						Toast.LENGTH_SHORT).show();
				Log.d("SendButton", "送信をキャンセルします");
			}
		});
		dialog.show();
	}

}
